﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Web;

namespace ThanhTu.GNCMedia.KQXS.Common
{
    public class MessageRouter
    {
        private string routesFilePath = string.Empty;
        private DateTime lastModifiedTime = new DateTime(2013, 1, 1);

        private List<Router> routers = null;

        public MessageRouter(string routesFilePath)
        {
            this.routesFilePath = routesFilePath;
        }

        public void LoadProperties(string propFilePath)
        {
            try
            {
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(propFilePath);
                routesFilePath = (xmlDoc.GetElementsByTagName("routing-table").Item(0) as XmlElement).FirstChild.Value;
            }
            catch (Exception ex) { throw ex; }
        }

        public void CreateRouters()
        {
            try
            {
                if (routers != null)
                { DeleteRouters(); }

                FileInfo fi = new FileInfo(routesFilePath);
                lastModifiedTime = fi.LastWriteTime;
                routers = LoadRouters(routesFilePath);
            }
            catch (Exception ex) { throw ex; }
        }

        public void DeleteRouters()
        {
            try
            {
                if (routers != null)
                { routers = null; }
            }
            catch (Exception ex) { throw ex; }
        }

        public bool RoutingFileUpdated()
        {
            try
            {
                FileInfo fi = new FileInfo(routesFilePath);
                DateTime modifiedTime = fi.LastWriteTime;

                if (lastModifiedTime != modifiedTime)
                { return true; }
            }
            catch (Exception ex) { throw ex; }

            return false;
        }

        public string LookUp(string msisdn)
        {
            string result = string.Empty;
            if (string.IsNullOrEmpty(msisdn)) { return result; }

            if (routers != null)
            {
                bool found = false;

                for (int i = 0; i < routers.Count && !found; i++)
                {
                    if (routers[i].Routes != null)
                    {
                        for (int j = 0; j < routers[i].Routes.Count && !found; j++)
                        {
                            if (!string.IsNullOrEmpty(routers[i].Routes[j].MSISDN))
                            {
                                if (Regex.IsMatch(msisdn, routers[i].Routes[j].MSISDN))
                                {
                                    result = routers[i].Name;
                                    found = true; ;
                                }
                            }
                        }
                    }
                }
            }

            return result;
        }

        /*public RouteInfo LookUp(MTDO vMTDO)
        {
            RouteInfo routeInfo = new RouteInfo(string.Empty, string.Empty);

            try
            {
                if (string.IsNullOrEmpty(vMTDO.MSISDN) ||
                    string.IsNullOrEmpty(vMTDO.ShortCode) ||
                    string.IsNullOrEmpty( vMTDO.MsgType))
                {
                    log.WarnFormat("Invalid msg: mtId={0}, msisdn={1}, shortCode={2}, msgType={3}, msgTitle={4}, msgBody={5}, destPort={6}, cpId={7}, svcId={8}, opId={9}, rxTime={10}, txTime={11}, txAttempts={12}, txStatus={13}",
                        vMTDO.MTID, vMTDO.MSISDN, vMTDO.ShortCode, vMTDO.MsgType, vMTDO.MsgTitle, vMTDO.MsgBody, vMTDO.DestPort, vMTDO.CPID, vMTDO.SvcID, vMTDO.OPID, vMTDO.RxTime,);
                    return routeInfo;
                }

                if (routers != null)
                {
                    for (int i = 0; i < routers.Count; i++)
                    {
                        if (!routers[i].MsgType.Equals(msg.MsgType.ToString()))
                        { continue; }

                        bool found = true;
                        string msgKeyword = string.Empty;

                        if (routers[i].Routes != null &&
                            routers[i].Routes.Count > 0)
                        {
                            found = false;

                            for (int j = 0; j < routers[i].Routes.Count; j++)
                            {
                                if (!string.IsNullOrEmpty(routers[i].Routes[j].MSISDN) &&
                                    !Regex.IsMatch(msg.MSISDN, routers[i].Routes[j].MSISDN))
                                { continue; }

                                if (!string.IsNullOrEmpty(routers[i].Routes[j].ShortCode) &&
                                    !Regex.IsMatch(msg.ShortCode, routers[i].Routes[j].ShortCode))
                                { continue; }

                                if (!string.IsNullOrEmpty(msg.Keyword))
                                { msgKeyword = msg.Keyword; }
                                else if (msg.MsgType == Message.MessageType.MO &&
                                    msg.ContentType == Message.MessageContentType.TEXT_GSM &&
                                    !string.IsNullOrEmpty(msg.Content))
                                {
                                    try
                                    {
                                        string msgContent = HttpUtility.UrlDecode(msg.Content, Encoding.UTF8);
                                        string[] items = null;
                                        items = Regex.Split(msgContent.ToUpper(), routers[i].Routes[j].SplitRule);

                                        if (items != null &&
                                            items.Length > 0)
                                        {
                                            if (string.IsNullOrEmpty(items[0]) &&
                                                items.Length > 1)
                                            { msgKeyword = items[1]; }
                                            else
                                            { msgKeyword = items[0]; }
                                        }
                                    }
                                    catch (Exception ex) { log.Error("Exception checking keyword: " + ex.Message); }
                                }

                                log.Logger.Log(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, log4net.Core.Level.Trace, "msgKeyword = " + msgKeyword, null);

                                if (routers[i].Routes[j].Keywords != null &&
                                    routers[i].Routes[j].Keywords.Length > 0)
                                {
                                    if (string.IsNullOrEmpty(msgKeyword))
                                    { continue; }

                                    bool matched = false;

                                    for (int k = 0; k < routers[i].Routes[j].Keywords.Length && !matched; k++)
                                    {
                                        if (Regex.IsMatch(msgKeyword, routers[i].Routes[j].Keywords[k]))
                                        { matched = true; }
                                    }

                                    if (!matched)
                                    {
                                        log.Logger.Log(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, log4net.Core.Level.Trace, "no keyword match", null);
                                        continue;
                                    }
                                }

                                found = true;
                            }
                        }

                        if (!found) { continue; }

                        routeInfo.DestinationName = routers[i].Name;
                        routeInfo.Keyword = msgKeyword;
                        log.Logger.Log(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, log4net.Core.Level.Trace, "Destination found: name = " + routers[i].Name + ", keyword = " + msgKeyword, null);

                        return routeInfo;
                    }

                    log.Logger.Log(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, log4net.Core.Level.Trace, "Destination not found", null);
                }                
            }
            catch (Exception ex) { log.Error("Exception searching for route: " + ex.Message); }

            return routeInfo;
        }*/

        private XmlDocument LoadDocument(string routesFilePath)
        {
            XmlDocument xmlDoc = null;

            try
            {
                xmlDoc = new XmlDocument();
                xmlDoc.Load(routesFilePath);
            }
            catch (Exception ex)
            {
                xmlDoc = null;
                throw ex;
            }

            return xmlDoc;
        }

        private List<Router> LoadRouters(string routesFilePath)
        {
            List<Router> routers = null;
            XmlDocument xmlDoc = LoadDocument(routesFilePath);

            if (xmlDoc != null)
            {
                try
                {
                    XmlNodeList xmlNodeList = xmlDoc.GetElementsByTagName("dest");

                    if (xmlNodeList != null &&
                        xmlNodeList.Count > 0)
                    {
                        routers = new List<Router>(xmlNodeList.Count);

                        for (int i = 0; i < xmlNodeList.Count; i++)
                        {
                            XmlElement xmlElement = xmlNodeList.Item(i) as XmlElement;
                            Router router = LoadRouter(xmlElement);
                            routers.Add(router);
                        }
                    }
                }
                catch (Exception ex)
                {
                    routers = null;
                    throw ex;
                }
            }

            return routers;
        }

        private Router LoadRouter(XmlElement xmlElement)
        {
            string destName = xmlElement.GetAttribute("name");
            string msgType = xmlElement.GetAttribute("type");
            List<Route> routes = null;

            try
            {
                XmlNodeList xmlNodeList = xmlElement.GetElementsByTagName("rte");

                if (xmlNodeList != null &&
                    xmlNodeList.Count > 0)
                {
                    routes = new List<Route>(xmlNodeList.Count);

                    for (int i = 0; i < xmlNodeList.Count; i++)
                    {
                        XmlElement xmlElelment1 = xmlNodeList.Item(i) as XmlElement;
                        string msisdn = GetElementValueByTagName(xmlElelment1, "msisdn");
                        string shortCode = GetElementValueByTagName(xmlElelment1, "short-code");
                        string[] keywords = null;
                        string tmpKeyword = GetElementValueByTagName(xmlElelment1, "keyword");

                        if (!string.IsNullOrEmpty(tmpKeyword))
                        {
                            tmpKeyword = tmpKeyword.ToUpper();
                            keywords = Regex.Split(tmpKeyword, "[,;]+");
                        }

                        string splitRule = GetElementValueByTagName(xmlElelment1, "split-rule");
                        if (string.IsNullOrEmpty(splitRule)) { splitRule = "[^A-Z0-9]+"; }
                        splitRule = splitRule.ToUpper();

                        Route route = new Route(msisdn, shortCode, keywords, splitRule);
                        routes.Add(route);
                    }
                }
            }
            catch (Exception ex) { throw ex; }

            return new Router(destName, msgType, routes);
        }

        private string GetElementValueByTagName(XmlElement xmlElement, string tagName)
        {
            string result = string.Empty;

            try
            {
                XmlNodeList xmlNodeList = xmlElement.GetElementsByTagName(tagName);

                if (xmlNodeList != null &&
                    xmlNodeList.Count > 0)
                {
                    result = xmlNodeList.Item(0).FirstChild.Value;
                }
            }
            catch (Exception ex) { throw ex; }

            return result;
        }

        private class Router
        {
            private string name = string.Empty;
            private string msgType = string.Empty;
            private List<Route> routes = null;

            public Router(string name, string msgType, List<Route> routes)
            {
                this.name = name;
                this.msgType = msgType;
                this.routes = routes;
            }

            public string Name { get { return name; } set { name = value; } }
            public string MsgType { get { return msgType; } set { msgType = value; } }
            public List<Route> Routes { get { return routes; } set { routes = value; } }
        }

        private class Route
        {
            private string msisdn = string.Empty;
            private string shortCode = string.Empty;
            private string[] keywords = null;
            private string splitRule = string.Empty;

            public Route(string msisdn, string shortCode, string[] keywords, string splitRule)
            {
                this.msisdn = msisdn;
                this.shortCode = shortCode;
                this.keywords = keywords;
                this.splitRule = splitRule;
            }

            public string MSISDN { get { return msisdn; } set { msisdn = value; } }
            public string ShortCode { get { return shortCode; } set { shortCode = value; } }
            public string[] Keywords { get { return keywords; } set { keywords = value; } }
            public string SplitRule { get { return splitRule; } set { splitRule = value; } }
        }

        public class RouteInfo
        {
            private string destName = string.Empty;
            private string keyword = string.Empty;

            public RouteInfo(string destName, string keyword)
            {
                this.destName = destName;
                this.keyword = keyword;
            }

            public string DestinationName { get { return destName; } set { destName = value; } }
            public string Keyword { get { return keyword; } set { keyword = value; } }
        }
    }
}
